[Java][Spring Boot] JPQLのクエリの作成方法
はじめに
前回はDAOでクエリを実行しました。 今回は、クエリの実行手段を複数紹介します。 JPA自体はJavaとデータベースを繋ぐものでSpringの機能ではありませんが、基本として必要なので紹介します。
環境
Mac OSX 10.10.5 Yosemite Eclipse Mars2 Java 8 Spring Boot 1.3.6 PostgreSQL 9.5.1
JPQLの手順
1.EntityManagerの用意
@PersistenceContext EntityManager entityManager;
これを元にクエリを作成します。 アプリ実行時に1つだけBeanに登録されるので、基本は@PresistenceContextで接続します。 複数箇所で接続するとエラーになるので記述場所に注意が必要です。 @Autowiredでも可能ですが制限があるようです。
2.クエリ作成
以下3種類の方法を紹介します。
方法① JPQLで作成
Query query = entityManager .createQuery("from Fruit where id = :id") .setParameter("id", 変数);
JPQLでクエリを作成する場合。setParameterで変数をセットできます。 2行目の「:id」には、3行目で設定したパラメータが適用されます。
方法② SQLで作成
Query query = entityManager .createNativeQuery("select * from Fruit where id = ?1 or id = ?2") .setParameter(1, 変数) .setParameter(2, 変数);
2行目、SQLでクエリを作成する場合。 2行目の「?1」には、3行目で設定したパラメータが適用されます。 上記ではパラメータの設定方法を変えましたが、JPQLでもSQLでも共通で使えます。
方法③ クエリアノテーションで作成
@NamedQuery( name="クエリ名", query="JPQLもしくはSQL" )
エンティティクラスにアノテートします。 queryに書いたものが、nameで呼ばれる訳ですね。 使用例については最後に書いておきます。
3.実行と取得
List<'クラス名'> list = query.getResultList(); Object result = query.getSingleResult();
1行目、クエリの実行結果をリストで取得しています。 2行目、実行結果を1件だけ取得します。 こちらは取得できなかったり、複数取得するとExceptionが発生します。
また、クエリ作成から取得までを下記の様に短縮可能です。
List<'クラス名'> list = entityManager .createQuery("from テーブル名 where id = :id") .setParameter("id",変数) .getResultList();
DAOでクエリアノテーションを使ってみる
テーブルを用意
CREATE TABLE fruit ( id VARCHAR(2) NOT NULL, name VARCHAR(10), price integer, PRIMARY KEY(id) ); INSERT INTO fruit VALUES ('1','apple',300), ('2','orange',200), ('3','banana',100);
postgres=# select * from fruit; id | name | price ----+--------+------- 1 | apple | 300 2 | orange | 200 3 | banana | 100 (3 rows)
エンティティ
package com.jpa.mydao; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import lombok.Data; @Entity @Data @Table(name="fruit") @NamedQueries({ @NamedQuery( name="findAllFruitWithName", query="SELECT f FROM Fruit f WHERE f.name LIKE :fruitName" ) , @NamedQuery( name="findLikeName", query="from Fruit where name LIKE :fruitName" ) }) public class Fruit { @Id private String id; private String name; private Integer price; }
基本は、15~18行目や20~23行目の様に@NamedQueryでJPQLかSQLをqueryに設定し、それらを呼び出す名称をnameに設定します。 14行目の様に、@NamedQueriesで囲めば複数のNamedQueryを設定可能です。 15~18行目、SQL。 20~23行目、PSQL。
DAOインターフェース
package com.jpa.mydao.dao; import java.io.Serializable; import java.util.List; public interface FruitDao <T> extends Serializable { public List<T> findAllFruitWithName(String name); public List<T> findLikeName(String name); }
分かりやすく、クエリ名と同じメソッド名で作成しました。
DAOオブジェクト
package com.jpa.mydao.dao; import java.util.List; import javax.persistence.EntityManager; import org.springframework.stereotype.Repository; import com.jpa.mydao.Fruit; @Repository public class FruitDaoJpql implements FruitDao<Fruit>{ private EntityManager entityManager; public FruitDaoJpql(EntityManager entityManager) { super(); this.entityManager = entityManager; } @Override public List<Fruit> findAllFruitWithName(String name) { List<Fruit> list = entityManager.createNamedQuery("findAllFruitWithName") .setParameter("fruitName", "%" + name + "%") .getResultList(); return list; } @Override public List<Fruit> findLikeName(String name) { List<Fruit> list = entityManager.createNamedQuery("findLikeName") .setParameter("fruitName", "%" + name + "%") .getResultList(); return list; } }
DAOインターフェースのメソッド:findAllFruitWithName。 23行目、エンティティで設定したクエリ名を呼んでいます。 24行目、クエリはLikeを使ったあいまい検索なので、パラメータのnameを%で囲んでいます。
DAOインターフェースのメソッド:findLikeName。 こちらも上記と同様ですね。
コントローラー
package com.jpa.mydao; import java.util.List; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.jpa.mydao.dao.FruitDaoJpql; @RestController public class EntityManagerController { @PersistenceContext EntityManager entityManager; FruitDaoJpql dao; @PostConstruct public void init() { dao = new FruitDaoJpql(entityManager); } /* SQL */ @RequestMapping(value="/findallwith", method=RequestMethod.GET) public List<Fruit> findAllWith(@RequestParam("name") String name) { List<Fruit> list = dao.findAllFruitWithName(name); return list; } /* JPQL */ @RequestMapping(value="/findlike", method=RequestMethod.GET) public List<Fruit> findLike(@RequestParam("name") String name) { List<Fruit> list = dao.findLikeName(name); return list; } }
24~26行目、EntityManagerをDAOに渡して準備完了。 残り2つのメソッドは同じ内容ですね。 無駄にも感じますが、分かりやすさ重視という事で...
ターミナルで実行
実行コマンドと結果①
テーブルのカラムnameから「pp」をあいまい検索した結果。
$ curl http://localhost:8080/findlike?name=pp [{"id":"1","name":"apple","price":300}]
実行コマンドと結果②
テーブルのカラムnameから「e」をあいまい検索した結果。
$ curl http://localhost:8080/findallwith?name=e [{"id":"1","name":"apple","price":300},{"id":"2","name":"orange","price":200}]
さいごに
クエリアノテーションは中々見ない書き方で、アノテーション地獄の未来が見えてきますが、流れを覚えてしまえば慣れかなと思います。